<!doctype html>



  


<html class="theme-next mist use-motion" lang="zh-Hans">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>



<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />















  
  
  <link href="/blog/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />




  
  
  
  

  
    
    
  

  

  

  

  

  
    
    
    <link href="//fonts.googleapis.com/css?family=Lato:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext" rel="stylesheet" type="text/css">
  






<link href="/blog/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/blog/css/main.css?v=5.1.0" rel="stylesheet" type="text/css" />


  <meta name="keywords" content="java,热部署,ClassLoader,Instrumentation," />








  <link rel="shortcut icon" type="image/x-icon" href="/blog/favicon.ico?v=5.1.0" />






<meta name="description" content="jvm 类加载机制加载器类型jvm中默认有三种加载器：  应用类加载器（AppClassLoader）：负责加载用户类路径（classpath）下的类。一般情况下是程序中的默认类加载器。 扩展类加载器（ExtClassLoader）：负责加载JDK/jre/lib/ext目录中，或由java.ext.dirs系统变量指定路径中的类库；开发者可以直接引用扩展类加载器。 启动类加载器（Bootstra">
<meta name="keywords" content="java,热部署,ClassLoader,Instrumentation">
<meta property="og:type" content="article">
<meta property="og:title" content="Java实现热部署">
<meta property="og:url" content="https://gitee.com/shuhao/2018/04/11/Java实现热部署/index.html">
<meta property="og:site_name" content="Niki">
<meta property="og:description" content="jvm 类加载机制加载器类型jvm中默认有三种加载器：  应用类加载器（AppClassLoader）：负责加载用户类路径（classpath）下的类。一般情况下是程序中的默认类加载器。 扩展类加载器（ExtClassLoader）：负责加载JDK/jre/lib/ext目录中，或由java.ext.dirs系统变量指定路径中的类库；开发者可以直接引用扩展类加载器。 启动类加载器（Bootstra">
<meta property="og:locale" content="zh-Hans">
<meta property="og:image" content="https://gitee.com/blog/images/java/jvm/classloader_replationship.jpg">
<meta property="og:image" content="https://gitee.com/blog/images/java/jvm/class_cycle.jpg">
<meta property="og:updated_time" content="2018-05-28T01:30:17.779Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Java实现热部署">
<meta name="twitter:description" content="jvm 类加载机制加载器类型jvm中默认有三种加载器：  应用类加载器（AppClassLoader）：负责加载用户类路径（classpath）下的类。一般情况下是程序中的默认类加载器。 扩展类加载器（ExtClassLoader）：负责加载JDK/jre/lib/ext目录中，或由java.ext.dirs系统变量指定路径中的类库；开发者可以直接引用扩展类加载器。 启动类加载器（Bootstra">
<meta name="twitter:image" content="https://gitee.com/blog/images/java/jvm/classloader_replationship.jpg">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/blog/',
    scheme: 'Mist',
    sidebar: {"position":"right","display":"post","offset":12,"offset_float":0,"b2t":false,"scrollpercent":false},
    fancybox: true,
    motion: true,
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="https://gitee.com/shuhao/2018/04/11/Java实现热部署/"/>





  <title> Java实现热部署 | Niki </title>
</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  





  <script type="text/javascript">
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?cb9f0585e406f878cb2f29c89ad3cdb7";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>










  
  
    
  

  <div class="container one-collumn sidebar-position-right page-post-detail ">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/blog/"  class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">Niki</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle">天道酬勤</p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/blog/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/blog/categories" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-categories"></i> <br />
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/blog/archives" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
            
            归档
          </a>
        </li>
      
        
        <li class="menu-item menu-item-tags">
          <a href="/blog/tags" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br />
            
            标签
          </a>
        </li>
      

      
    </ul>
  

  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
    <link itemprop="mainEntityOfPage" href="https://gitee.com/shuhao/blog/2018/04/11/Java实现热部署/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Fang ShuHao">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/head.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Niki">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
            
            
              
                Java实现热部署
              
            
          </h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-04-11T20:00:53+08:00">
                2018-04-11
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/java/" itemprop="url" rel="index">
                    <span itemprop="name">java</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    


    <div class="post-body" itemprop="articleBody">

      
      

      
        <h2 id="jvm-类加载机制"><a href="#jvm-类加载机制" class="headerlink" title="jvm 类加载机制"></a>jvm 类加载机制</h2><h4 id="加载器类型"><a href="#加载器类型" class="headerlink" title="加载器类型"></a>加载器类型</h4><p>jvm中默认有三种加载器：</p>
<ol>
<li>应用类加载器（AppClassLoader）：负责加载用户类路径（classpath）下的类。一般情况下是程序中的默认类加载器。</li>
<li>扩展类加载器（ExtClassLoader）：负责加载JDK/jre/lib/ext目录中，或由java.ext.dirs系统变量指定路径中的类库；开发者可以直接引用扩展类加载器。</li>
<li>启动类加载器（BootstrapClassloader）：负责加载JDK/jre/lib目录下的类库，启动类加载器无法被java程序直接使用。</li>
</ol>
<p>这三个类加载器的关系为：启动类加载器是扩展类加载器的父加载器，扩展类加载器是应用类加载器的父加载器。这里的父子关系不是java中的继承关系。类似于父子容器的关系。</p>
<h4 id="类加载机制"><a href="#类加载机制" class="headerlink" title="类加载机制"></a>类加载机制</h4><p>JVM中，类加载的机制有：</p>
<ol>
<li>双亲委托：先让父加载器加载类，如果父加载器无法加载类时才从自己的路径中加载类。</li>
<li>全盘负责：当一个类加载器负责加载某个class时，该class锁依赖和引用的其他class将由该类加载器负责载入，除非显示使用另一个类加载器来载入。</li>
<li>缓存机制：jvm会缓存加载的所有的加载过的class类，当程序需要class时，类加载机制会先从缓存中查找class。当缓存不存在，相应的类加载器才会去相应的路径中加载对应的class进来，并且缓存。这也是为什么修改了java代码需要重启JVM。</li>
</ol>
<h4 id="类加载机制的意义"><a href="#类加载机制的意义" class="headerlink" title="类加载机制的意义"></a>类加载机制的意义</h4><ol>
<li>双亲加载可以保护我们的JVM运行环境。假如，我们加载了一个和JDK路径一样的String类对象。加载时必须要先调用父加载器，我们知道String类对象已经在BootStartClassLoader加载器中加载，所以不会再次重新加载。因此保护了我们的运行环境。</li>
<li>全盘负责机制，可以保证我们的类正常运行，如果我们只加载本身的类对象，而不去加载其依赖以及引用的类对象，必然在调用时产生异常。</li>
<li>缓存机制我觉得应该是在我们初始化对象的时候提高初始化性能，不用每次初始化都去重新加载，连接类对象。</li>
</ol>
<img src="/blog/images/java/jvm/classloader_replationship.jpg">
<h4 id="JVM如何加载类"><a href="#JVM如何加载类" class="headerlink" title="JVM如何加载类"></a>JVM如何加载类</h4><p>JVM加载类主要分为以下三个步骤：</p>
<h6 id="加载类："><a href="#加载类：" class="headerlink" title="加载类："></a>加载类：</h6><ol>
<li>加载器加载类的字节码</li>
<li>将字节流中所代表的静态存储接口转化为方法区的运行时数据结构</li>
<li>在<strong>堆中</strong>生成相应的Class对象，作为方法去中这些数据的访问入口。 </li>
</ol>
<h6 id="连接类"><a href="#连接类" class="headerlink" title="连接类"></a>连接类</h6><ol>
<li>验证被加载的类的正确性。主要验证其文件格式，元数据，字节码以及符号引用</li>
<li>准备：为类的静态变量分配内存，并初始化默认值（注意并不是静态变量所赋的值）。引用类型赋值null；如果是常量则为代码中的初始值。</li>
<li>解析：把类中的符号引用转换为直接引用</li>
</ol>
<h6 id="初始化"><a href="#初始化" class="headerlink" title="初始化"></a>初始化</h6><p>前面说的，类连接过程中会为静态变量分配内存，但不会为其赋真正的值，那么初始化就是为类的静态变量赋初始值。<br>类的初始化步骤为：</p>
<ol>
<li>先加载，连接类该类</li>
<li>如果该类的直接父类没有初始化，先初始化父类</li>
<li>如果类中有初始化语句，则依次执行类中的初始化语句</li>
</ol>
<img src="/blog/images/java/jvm/class_cycle.jpg">
<h2 id="实现加载器，加载外部class"><a href="#实现加载器，加载外部class" class="headerlink" title="实现加载器，加载外部class"></a>实现加载器，加载外部class</h2><p>启动java项目时，我们的类都由java默认的类加载器加载好。那么在项目中，如果我们需要加载外部class，该如何进行？<br>1、Java给我们提供了一个类加载器对象ClassLoader我们通过继承该对象实现自己的类加载器。并且提供了方法loadClass方法<br>我们先看下面的两段代码：<br><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ClassLoaderDemo1</span> <span class="keyword">extends</span> <span class="title">ClassLoader</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">loadClassTest</span><span class="params">()</span> <span class="keyword">throws</span> ClassNotFoundException, NoSuchFieldException, IllegalAccessException </span>&#123;</span><br><span class="line">        Class defaultClass = User.class;</span><br><span class="line">        Class loadClass = loadClass(<span class="string">"jvm.dynamic_load.User"</span>);</span><br><span class="line">        System.out.println(<span class="string">"java默认加载器加载类"</span> + defaultClass.getName());</span><br><span class="line">        System.out.println(<span class="string">"自定义加载器加载类"</span> + loadClass.getName());</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">"java默认加载器"</span> + defaultClass.getClassLoader());</span><br><span class="line">        System.out.println(<span class="string">"自定义加载器"</span> + loadClass.getClassLoader());</span><br><span class="line"></span><br><span class="line">        User.code = <span class="number">11</span>;</span><br><span class="line">        System.out.println(<span class="string">"java默认加载器加载类"</span> + getStaticField(defaultClass));</span><br><span class="line">        System.out.println(<span class="string">"自定义加载器加载类"</span> + getStaticField(loadClass));</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">"两个类对象是否相等："</span> + (defaultClass == loadClass));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>运行的结果：<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">java默认加载器加载类jvm.dynamic_load.User</span><br><span class="line">自定义加载器加载类jvm.dynamic_load.User</span><br><span class="line">java默认加载器sun.misc.Launcher$AppClassLoader@18b4aac2</span><br><span class="line">自定义加载器sun.misc.Launcher$AppClassLoader@18b4aac2</span><br><span class="line">java默认加载器加载类11</span><br><span class="line">自定义加载器加载类11</span><br><span class="line">两个类对象是否相等：true</span><br></pre></td></tr></table></figure></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ClassLoaderDemo1</span> <span class="keyword">extends</span> <span class="title">ClassLoader</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">defineClassTest</span><span class="params">()</span> <span class="keyword">throws</span> IOException, NoSuchFieldException, IllegalAccessException </span>&#123;</span><br><span class="line">        Class defaultClass = User.class;</span><br><span class="line">        <span class="keyword">byte</span>[] classBytes = ClassLoaderDemo1.getClassBytes(User.class.getName());</span><br><span class="line">        Class loadClass = defineClass(User.class.getName(), classBytes, <span class="number">0</span>, classBytes.length);</span><br><span class="line">        System.out.println(<span class="string">"java默认加载器加载类"</span> + defaultClass.getName());</span><br><span class="line">        System.out.println(<span class="string">"自定义加载器加载类"</span> + loadClass.getName());</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">"java默认加载器"</span> + defaultClass.getClassLoader());</span><br><span class="line">        System.out.println(<span class="string">"自定义加载器"</span> + loadClass.getClassLoader());</span><br><span class="line"></span><br><span class="line">        User.code = <span class="number">11</span>;</span><br><span class="line">        System.out.println(<span class="string">"java默认加载器加载类"</span> + getStaticField(defaultClass));</span><br><span class="line">        System.out.println(<span class="string">"自定义加载器加载类"</span> + getStaticField(loadClass));</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">"两个类对象是否相等："</span> + (defaultClass == loadClass));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>运行的结果：<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">java默认加载器加载类jvm.dynamic_load.User</span><br><span class="line">自定义加载器加载类jvm.dynamic_load.User</span><br><span class="line">java默认加载器sun.misc.Launcher$AppClassLoader@18b4aac2</span><br><span class="line">自定义加载器jvm.dynamic_load.ClassLoaderDemo1@2b193f2d</span><br><span class="line">java默认加载器加载类11</span><br><span class="line">自定义加载器加载类10</span><br><span class="line">两个类对象是否相等：false</span><br></pre></td></tr></table></figure></p>
<p>我们看到同样是加载类文件目录（classpath）下的User的字节码，但是使用loadClass方法加载和使用defineClass方法加载的类对象与java默认加载的类对象完全不一样。</p>
<ul>
<li>使用loadClass方法加载的类对象和java默认加载的类对象是同一个对象。因为，使用loadClass加载对象的机制就是<strong>双亲委托</strong>以及<strong>缓存机制</strong>，所以，在调用loadClass的时候，并不是使用ClassLoaderDemo1中的加载方法，而使用的是其父加载器，也就是AppClassLoader的加载器。并且因为jvm启动的时候，已经加载了User的类对象，并且缓存下来，所以再次加载去读缓存，而不是重新加载。</li>
<li>当我们使用defineClass的时候，可以看到，并没有去调用父加载器的加载方法，而是重新在类加载器（ClassLoaderDemo1）中加载了一个新的User类对象 。因此，如果我们需要实现加载新的Class对象，应该使用defineClass方法。</li>
</ul>
<h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><p>所以，当我们需要动态加载外部的class对象时，只需要获取该class文件的byte数组，使用defineClass加载Class对象，然后通过反射调用的方式使用该类。</p>
<h2 id="创建class字节码。"><a href="#创建class字节码。" class="headerlink" title="创建class字节码。"></a>创建class字节码。</h2><p>上面我们看到可以通过defineClass方法加载字节码文件的byte[]来装载类，那么我们可以使用第三方的工具在程序中创建字节码然后加载使用。主要的第三方工具有：javasists、cglib<br><a href="https://github.com/jboss-javassist/javassist/wiki/Tutorial-1" target="_blank" rel="external">具体javassist的使用可以参考文档</a></p>
<h2 id="Java-Instrumentation动态重定义Class"><a href="#Java-Instrumentation动态重定义Class" class="headerlink" title="Java Instrumentation动态重定义Class"></a>Java Instrumentation动态重定义Class</h2><h4 id="Instrumentation介绍"><a href="#Instrumentation介绍" class="headerlink" title="Instrumentation介绍"></a>Instrumentation介绍</h4><p>Java从JavaSE5开始提供了Instrumentation。可供开发者用来构建一个独立与应用程序的代理程序（Agent），用来监测和协助运行在JVM上的程序。甚至能够替换和修改某些类的定义。因此开发者能够更加灵活的监控Java类操作以及修改JVM加载的类。<br>Instrument实际上类似于一种AOP的功能。</p>
<h4 id="实现方式"><a href="#实现方式" class="headerlink" title="实现方式"></a>实现方式</h4><ol>
<li>JavaSE5中，Instrumentation需要在程序运行之前，通过参数设置。我们称这个方式为<strong>permain</strong>方式。如下：读者可以自行了解该方式,<a href="https://blog.csdn.net/ykdsg/article/details/12080071" target="_blank" rel="external">可参考</a><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">java -javaagent:jar 文件的位置 [= 传入 premain 的参数 ] -jar SomeJavaClass</span><br></pre></td></tr></table></figure>
</li>
</ol>
<p>注意：-javaagent 一定要早 -jar之前设置。</p>
<ol>
<li>上一种方式在main方法执行之前进行代理。在JavaSE6中，java提供了更加强大的Instrumentation功能，可以在JVM启动后，main方法执行以后，使用Instrumentation代理。甚至修在已经加载的class。我们称这种方式为<strong>agentmain</strong>方式</li>
</ol>
<h4 id="agentmain方式"><a href="#agentmain方式" class="headerlink" title="agentmain方式"></a>agentmain方式</h4><p>通过agentmain方式实现代理连接访问JVM和动态修改class的方式有如下几个步骤：</p>
<ol>
<li>创建代理jar<br>我们创建如下的代理类，并且打成jar包<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JavaDynAgent</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> Instrumentation instrumentation;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> Object lockObject = <span class="keyword">new</span> Object();</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">JavaDynAgent</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">agentmain</span><span class="params">(String args, Instrumentation ins)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (lockObject) &#123;</span><br><span class="line">            <span class="keyword">if</span> (JavaDynAgent.instrumentation == <span class="keyword">null</span>) &#123;</span><br><span class="line">                JavaDynAgent.instrumentation = ins;</span><br><span class="line">                System.out.println(<span class="string">"&gt;&gt;&gt;&gt;java agent 代理：init instrumentation"</span>);</span><br><span class="line">                System.out.println(instrumentation == <span class="keyword">null</span> ? <span class="string">"&gt;&gt;&gt;&gt;null"</span> : <span class="string">"&gt;&gt;&gt;&gt;"</span> + instrumentation.toString());</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                System.out.println(<span class="string">"&gt;&gt;&gt;&gt;instrumentation is inited"</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Instrumentation <span class="title">getInstrumentation</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> JavaDynAgent.instrumentation;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
</ol>
<p>生成META-INF文件如下<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">Manifest-Version: 1.0</span><br><span class="line">Agent-Class: com.totoro.agent.JavaDynAgent</span><br><span class="line">Can-Retransform-Classes: true</span><br><span class="line">Can-Redefine-Classes: true</span><br></pre></td></tr></table></figure></p>
<p>建议可以使用maven成jar包。pom中的配置如下：<br><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span><br><span class="line"><span class="tag">&lt;<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">"http://maven.apache.org/POM/4.0.0"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xmlns:xsi</span>=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xsi:schemaLocation</span>=<span class="string">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.totoro<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>agent<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">description</span>&gt;</span>java动态修改class的代理jar<span class="tag">&lt;/<span class="name">description</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">build</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugins</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.maven.plugins<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>maven-jar-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.4<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">configuration</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">archive</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">manifest</span>&gt;</span></span><br><span class="line">                            <span class="tag">&lt;<span class="name">addClasspath</span>&gt;</span>true<span class="tag">&lt;/<span class="name">addClasspath</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;/<span class="name">manifest</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">manifestEntries</span>&gt;</span></span><br><span class="line">                            <span class="tag">&lt;<span class="name">Agent-Class</span>&gt;</span>com.totoro.agent.JavaDynAgent<span class="tag">&lt;/<span class="name">Agent-Class</span>&gt;</span></span><br><span class="line">                            <span class="tag">&lt;<span class="name">Can-Retransform-Classes</span>&gt;</span>true<span class="tag">&lt;/<span class="name">Can-Retransform-Classes</span>&gt;</span></span><br><span class="line">                            <span class="tag">&lt;<span class="name">Can-Redefine-Classes</span>&gt;</span>true<span class="tag">&lt;/<span class="name">Can-Redefine-Classes</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;/<span class="name">manifestEntries</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">archive</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugins</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;/<span class="name">build</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure></p>
<ol>
<li>加载代理jar<br>在main方法启动后，如何加载我们的代理jar呢？这里使用的java提供的另一个api—— Attach </li>
</ol>
<h4 id="Attach原理"><a href="#Attach原理" class="headerlink" title="Attach原理"></a>Attach原理</h4><p>Attach中主要两个类：VirtuamMachine、AttachProvider。</p>
<ul>
<li>AttachProvider：<br>该类主要实现是，针对不同的操作系统提供不同的可以访问的VirtualMachine。如：Window使用的是：WindowsAttachProvider；linux使用的是LinuxAttachProvider；Maco使用的是MacosxAttachProvider。</li>
<li><p>VirtualMachine：<br>VirtualMachine代表的是一个JVM进程。通过AttachProvider获取复合操作系统的VirtualMachine，并且attach正在运行的JVM的进程pid，就得到了当前JVM的一个实例。就可以通过该VirtualMachine实例加载代理。实现Instrumentation的功能。<br>具体的实现代码如下：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">VirtualMachine virtualMachine = <span class="keyword">null</span> ;</span><br><span class="line"> <span class="keyword">try</span> &#123;</span><br><span class="line">       <span class="comment">//pid为JVM运行的进程号</span></span><br><span class="line">        virtualMachine = VirtualMachine.attach(pid);</span><br><span class="line">       	<span class="comment">//agentPath 为代理jar的文件路径</span></span><br><span class="line">        virtualMachine.loadAgent(agentPath);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (AttachNotSupportedException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125; <span class="keyword">catch</span> (AgentLoadException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125; <span class="keyword">catch</span> (AgentInitializationException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125;<span class="keyword">finally</span>&#123;</span><br><span class="line">    	virtualMachine.detach();</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>获取当前JVM进程pid的方法：<br>我们可以通过jps查看当前机器上所有运行的进程pid，可以通过下列代码获取当前JVM的进程pid</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">String name = ManagementFactory.getRuntimeMXBean().getName();</span><br><span class="line">pid = name.split(<span class="string">"@"</span>)[<span class="number">0</span>];</span><br></pre></td></tr></table></figure>
</li>
</ul>
<p><strong>注意</strong><br>我们用到的VirtualMachine类并在java暴露的jdk中，在JAVA_HOME/lib/tools.jar中。如果要使用该类，则程序需要引入该jar。如果是maven程序可以如下引入：<br><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.sun<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>tools<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.8.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">scope</span>&gt;</span>system<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">systemPath</span>&gt;</span>$&#123;env.JAVA_HOME&#125;/lib/tools.jar<span class="tag">&lt;/<span class="name">systemPath</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure></p>
<p>这种引用在开发环境时可以，当我们需要将应用打包时发现，tools.jar没有打到我们的war或者jar中。主要的原因还是因为其scope类型。感兴趣的同学可以自行了解maven的scope类型<br>因此，我们只能将tools.jar发布到我们的maven库中，然后引入我们maven库中的tools.jar。类似于Oracle数据库驱动的方式。<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">mvn install:install-file -Dfile=JAVA_HOME/lib/tools.jar -DgroupId=com.kingdee.utils  -DartifactId=java_tools  -Dversion=1.8.0  -Dpackaging=jar</span><br></pre></td></tr></table></figure></p>
<p>然后在工程中引用即可解决无法将tools.jar打包的问题了<br><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.kingdee.utils<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>java_tools<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.8.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure></p>
<ol>
<li>动态重定义class<br>要实现修改已加载的class，主要还是用到Instrumentation的功能。<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ClassDefinition</span><span class="params">(    Class&lt;?&gt; theClass, <span class="keyword">byte</span>[]  theClassFile)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (theClass == <span class="keyword">null</span> || theClassFile == <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException();</span><br><span class="line">    &#125;</span><br><span class="line">    mClass      = theClass;</span><br><span class="line">    mClassFile  = theClassFile;</span><br><span class="line">    &#125;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">redefineClasses</span><span class="params">(ClassDefinition... definitions)</span> <span class="keyword">throws</span>  ClassNotFoundException, UnmodifiableClassException</span>;</span><br></pre></td></tr></table></figure>
</li>
</ol>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">javaAgent</span><span class="params">(String root, String[] classArr)</span> </span>&#123;</span><br><span class="line">    init();</span><br><span class="line">    List&lt;ClassDefinition&gt; classDefinitions = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">    <span class="keyword">for</span> (String className : classArr) &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Class&lt;?&gt; c = Class.forName(className);</span><br><span class="line">            String classPath = (!StringUtils.isEmpty(root) ? root : classesPath) + className.replace(<span class="string">"."</span>, <span class="string">"/"</span>) + <span class="string">".class"</span>;</span><br><span class="line">            LOGGER.info(<span class="string">"class redefined:&#123;&#125;"</span>, classPath);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">byte</span>[] bytesFromFile = getBytes(classPath);</span><br><span class="line">            ClassDefinition classDefinition = <span class="keyword">new</span> ClassDefinition(c, bytesFromFile);</span><br><span class="line">            classDefinitions.add(classDefinition);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (ClassNotFoundException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                destroy();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        getInstrumentation().redefineClasses(classDefinitions.toArray(<span class="keyword">new</span> ClassDefinition[classDefinitions.size()]));</span><br><span class="line">    &#125; <span class="keyword">catch</span> (ClassNotFoundException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125; <span class="keyword">catch</span> (UnmodifiableClassException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>上面的代码主要调用的是Instrumentation的热defineClasses方法。该方法的参数是一个ClassDefinition数组。ClassDefinition由class名称以及class字节码组成。调用该函数，则动态修改class内容。转为新的class。从而实现了动态修改class的功能</p>
<p>ClassLoader.load()，然后通过反射来调用Class的方法时，会生成其实例。每次进行这样的操作都会生成一个ClassLoader和该类加载器对应的类对象，之前的类加载器和对象则会被断开连接（unlinked）并且被垃圾回收机制回收</p>
<p><a href="https://gitee.com/shuhao/code/tree/master/java-template/src/main/java/jvm/instrumentation/dynamic_change_class">代码</a></p>
<ol>
<li>动态代理Class<br>我们除了上面的在JVM中显示修改类class定义外，我们还可以在agent所属的jar包内代理我们的jvm加载的类。添加类运行监控的功能。这种功能就是之前的premain的功能。我们看下监控的Agent的jar包如何编写</li>
</ol>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Agent类，用来获取Instrumentation对象</span></span><br><span class="line"><span class="comment"> * Created by Niki on 2018/4/18 14:31</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TimeMonitorAgent</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> Instrumentation instrumentation;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> Object lock = <span class="keyword">new</span> Object();</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">TimeMonitorAgent</span><span class="params">()</span> </span>&#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">agentmain</span><span class="params">(String args, Instrumentation instrumentation)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (lock) &#123;</span><br><span class="line">            <span class="keyword">if</span> (instrumentation == <span class="keyword">null</span>) &#123;</span><br><span class="line">                TimeMonitorAgent.instrumentation = instrumentation;</span><br><span class="line">                System.out.println(<span class="string">"init instrumentation"</span>);</span><br><span class="line"></span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                System.out.println(<span class="string">"instrumentation is inited "</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        ClassFileTransformer transformer = <span class="keyword">new</span> TimeMonitorTransformet();</span><br><span class="line"></span><br><span class="line">        <span class="comment">/* 将transformer注册用来改变类的方法，增加监控时间的功能 */</span></span><br><span class="line">        TimeMonitorAgent.instrumentation.addTransformer(transformer);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 自定义ClassFileTransformer，用来修改字节码 增加方法调用时长监控</span></span><br><span class="line"><span class="comment"> * Created by Niki on 2018/4/18 9:14</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TimeMonitorTransformet</span>  <span class="keyword">implements</span> <span class="title">ClassFileTransformer</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">byte</span>[] transform(ClassLoader loader, String className, Class&lt;?&gt; classBeingRedefined, ProtectionDomain protectionDomain, <span class="keyword">byte</span>[] classfileBuffer) <span class="keyword">throws</span> IllegalClassFormatException &#123;</span><br><span class="line">        <span class="keyword">byte</span>[] transformed = <span class="keyword">null</span>;</span><br><span class="line">        ClassPool pool = <span class="keyword">null</span>;</span><br><span class="line">        CtClass ctClass = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            pool = ClassPool.getDefault();</span><br><span class="line">            ctClass = pool.makeClass(<span class="keyword">new</span> ByteArrayInputStream(classfileBuffer));</span><br><span class="line">            CodeConverter convert = <span class="keyword">new</span> CodeConverter();</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (ctClass.isInterface() == <span class="keyword">false</span>) &#123;</span><br><span class="line">                CtMethod[] methods = ctClass.getDeclaredMethods();</span><br><span class="line">                <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; methods.length; i++) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (!methods[i].isEmpty()) &#123;</span><br><span class="line">                        AOPInsertMethod(methods[i]);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                transformed = ctClass.toBytecode();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (CannotCompileException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (ctClass != <span class="keyword">null</span>) &#123;</span><br><span class="line">                ctClass.detach();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> transformed;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 为方法添加时间监控</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> method</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">AOPInsertMethod</span><span class="params">(CtMethod method)</span> <span class="keyword">throws</span> CannotCompileException </span>&#123;</span><br><span class="line">        method.instrument(<span class="keyword">new</span> ExprEditor()&#123;</span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">edit</span><span class="params">(MethodCall m)</span> <span class="keyword">throws</span> CannotCompileException </span>&#123;</span><br><span class="line">                m.replace(<span class="string">"&#123; long stime = System.currentTimeMillis(); $_ = $proceed($$);System.out.println(\""</span></span><br><span class="line">                        + m.getClassName() + <span class="string">"."</span> + m.getMethodName()</span><br><span class="line">                        + <span class="string">" cost:\" + (System.currentTimeMillis() - stime) + \" ms\");&#125;"</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="comment">//在方法体前后添加语句</span></span><br><span class="line"><span class="comment">//        method.insertBefore("System.out.println(\"enter method\"+System.currentTimeMillis());");</span></span><br><span class="line"><span class="comment">//        method.insertAfter("System.out.println(\"leave method\"+System.currentTimeMillis()););");</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>在需要监控的jar包运行之前加上java参数<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">java -javaagent:E:/workspace/TimeMonitorAgent-1.0.0.jar -cp E:/workspace/SayHello/target/SayHello-0.0.1-SNAPSHOT.jar com.jdktest.SayHello.Target</span><br></pre></td></tr></table></figure></p>
<p>这个功能类似于spring的AOP，在类执行之前动态的添加代理。主要的实现功能还是在TimeMonitorTransformet类中，该类是ClassFileTransformer接口的实现类。<br>我们看下官网对这个类的解释：<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">An agent provides an implementation of this interface in order to transform class files. The transformation occurs before the class is defined by the JVM.</span><br></pre></td></tr></table></figure></p>
<p>很简单的一句话，大概的意思是，这个是代理实现的接口，这个代理的作用是重新定义类文件，并且是发生在JVM加载类文件之前。<br><a href="https://gitee.com/shuhao/code/tree/master/java-template/src/main/java/jvm/instrumentation/method_time_monito">代码</a></p>
<p>好了，这篇文章总结的比较多，从java类的加载到Java的Agent的机制，以及其主要的实现方式以及用途。希望小伙伴们共同进步。</p>
<ol>
<li><p>双亲委托机制</p>
</li>
<li><p>不同的加载器生成不同的类对象。unlink的类加载器以及该类加载器生成的java对象都会被GC</p>
</li>
<li><p>多个类链条关联。类似于垃圾回收机制中的类可达确认。如果回收root对象，则其连接的对象都应该被回收，如何保证其会回收，因为类与类加载器的关系是双向的，类加载器会缓存器加载的所有class</p>
</li>
<li><p>如何区分需要被动态加载的类以及不需要被动态加载的类。不需要被动态加载的类则使用java默认的类加载器加载。默认的类加载器会缓存其加载的所有Class对象。这也是为什么修改了类之后必须重启java的原因</p>
</li>
</ol>
<blockquote>
<blockquote>
<p>参考<br><a href="https://blog.csdn.net/yczz/article/details/51656386" target="_blank" rel="external">Instrumentation 实践详解</a><br><a href="http://nijiaben.iteye.com/blog/1846595" target="_blank" rel="external">浅谈JVM中的Instrumentation的实现</a><br><a href="https://www.cnblogs.com/orionhp/p/6362625.html" target="_blank" rel="external">Instrumentation介绍</a></p>
</blockquote>
</blockquote>

      
    </div>

    <div>
      
        

      
    </div>

    <div>
      
        

      
    </div>


    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/blog/tags/java/" rel="tag"># java</a>
          
            <a href="/blog/tags/热部署/" rel="tag"># 热部署</a>
          
            <a href="/blog/tags/ClassLoader/" rel="tag"># ClassLoader</a>
          
            <a href="/blog/tags/Instrumentation/" rel="tag"># Instrumentation</a>
          
        </div>
      

      
        
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/blog/2018/04/11/sql查询优化—limit/" rel="next" title="sql查询优化—limit">
                <i class="fa fa-chevron-left"></i> sql查询优化—limit
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/blog/2018/04/16/redis—setbit和bitcount/" rel="prev" title="redis—setbit和bitcount">
                redis—setbit和bitcount <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </article>



    <div class="post-spread">
      
    </div>
  </div>

          
          </div>
          


          
  <div class="comments" id="comments">
    
      <div id="lv-container" data-id="city" data-uid="MTAyMC8zNjY5NS8xMzIzMA=="></div>
    
  </div>


        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap" >
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
          <img class="site-author-image" itemprop="image"
               src="/blog/images/head.jpg"
               alt="Fang ShuHao" />
          <p class="site-author-name" itemprop="name">Fang ShuHao</p>
           
              <p class="site-description motion-element" itemprop="description"></p>
          
        </div>
        <nav class="site-state motion-element">
        
          
            <div class="site-state-item site-state-posts">
              <a href="/blog/archives">
                <span class="site-state-item-count">135</span>
                <span class="site-state-item-name">日志</span>
              </a>
            </div>
          

          
            <div class="site-state-item site-state-categories">
              <a href="/blog/categories">
                <span class="site-state-item-count">35</span>
                <span class="site-state-item-name">分类</span>
              </a>
            </div>
          

          
            <div class="site-state-item site-state-tags">
              <a href="/blog/tags">
                <span class="site-state-item-count">93</span>
                <span class="site-state-item-name">标签</span>
              </a>
            </div>
          

        </nav>

        

        <div class="links-of-author motion-element">
          
        </div>

        
        

        
        

        


      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#jvm-类加载机制"><span class="nav-number">1.</span> <span class="nav-text">jvm 类加载机制</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#加载器类型"><span class="nav-number">1.0.1.</span> <span class="nav-text">加载器类型</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#类加载机制"><span class="nav-number">1.0.2.</span> <span class="nav-text">类加载机制</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#类加载机制的意义"><span class="nav-number">1.0.3.</span> <span class="nav-text">类加载机制的意义</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#JVM如何加载类"><span class="nav-number">1.0.4.</span> <span class="nav-text">JVM如何加载类</span></a><ol class="nav-child"><li class="nav-item nav-level-6"><a class="nav-link" href="#加载类："><span class="nav-number">1.0.4.0.1.</span> <span class="nav-text">加载类：</span></a></li><li class="nav-item nav-level-6"><a class="nav-link" href="#连接类"><span class="nav-number">1.0.4.0.2.</span> <span class="nav-text">连接类</span></a></li><li class="nav-item nav-level-6"><a class="nav-link" href="#初始化"><span class="nav-number">1.0.4.0.3.</span> <span class="nav-text">初始化</span></a></li></ol></li></ol></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#实现加载器，加载外部class"><span class="nav-number">2.</span> <span class="nav-text">实现加载器，加载外部class</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#总结"><span class="nav-number">2.0.1.</span> <span class="nav-text">总结</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#创建class字节码。"><span class="nav-number">3.</span> <span class="nav-text">创建class字节码。</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Java-Instrumentation动态重定义Class"><span class="nav-number">4.</span> <span class="nav-text">Java Instrumentation动态重定义Class</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#Instrumentation介绍"><span class="nav-number">4.0.1.</span> <span class="nav-text">Instrumentation介绍</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#实现方式"><span class="nav-number">4.0.2.</span> <span class="nav-text">实现方式</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#agentmain方式"><span class="nav-number">4.0.3.</span> <span class="nav-text">agentmain方式</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#Attach原理"><span class="nav-number">4.0.4.</span> <span class="nav-text">Attach原理</span></a></li></ol></li></ol></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright" >
  
  &copy; 
  <span itemprop="copyrightYear">2019</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Fang ShuHao</span>
</div>


<div class="powered-by">
  由 <a class="theme-link" href="https://hexo.io">Hexo</a> 强力驱动
</div>

<div class="theme-info">
  主题 -
  <a class="theme-link" href="https://github.com/iissnan/hexo-theme-next">
    NexT.Mist
  </a>
</div>


        

        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    
    
  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  




  
  <script type="text/javascript" src="/blog/lib/jquery/index.js?v=2.1.3"></script>

  
  <script type="text/javascript" src="/blog/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>

  
  <script type="text/javascript" src="/blog/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>

  
  <script type="text/javascript" src="/blog/lib/velocity/velocity.min.js?v=1.2.1"></script>

  
  <script type="text/javascript" src="/blog/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>

  
  <script type="text/javascript" src="/blog/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>


  


  <script type="text/javascript" src="/blog/js/src/utils.js?v=5.1.0"></script>

  <script type="text/javascript" src="/blog/js/src/motion.js?v=5.1.0"></script>



  
  

  
  <script type="text/javascript" src="/blog/js/src/scrollspy.js?v=5.1.0"></script>
<script type="text/javascript" src="/blog/js/src/post-details.js?v=5.1.0"></script>



  


  <script type="text/javascript" src="/blog/js/src/bootstrap.js?v=5.1.0"></script>



  



  




	





  





  





  
    <script type="text/javascript">
      (function(d, s) {
        var j, e = d.getElementsByTagName(s)[0];
        if (typeof LivereTower === 'function') { return; }
        j = d.createElement(s);
        j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
        j.async = true;
        e.parentNode.insertBefore(j, e);
      })(document, 'script');
    </script>
  



  
  

  

  

  

  


  

</body>
</html>
